#!/bin/sh
################################################################################
# simplest.qos (Cero3 Simple Shaper)
#
# Abstract:
# This is a single band fq_codel and ipv6 enabled shaping script for Ethernet
# gateways. This is nearly the simplest possible. With FQ_CODEL, the sparseness
# priority will work pretty well for a casual network. Flow-hashes should not
# overlap much with only a few users.
#
# References:
# This alternate shaper attempts to go for 1/u performance in a clever way
# http://git.coverfire.com/?p=linux-qos-scripts.git;a=blob;f=src-3tos.sh;hb=HEAD
#
################################################################################
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
#  Copyright (C) 2012-2016
#    Michael D. Taht, Toke Høiland-Jørgensen, Sebastian Moeller
#
################################################################################

. ${SQM_LIB_DIR}/defaults.sh

################################################################################

cake_egress()
{
    $TC qdisc add dev $IFACE root `get_stab_string` cake bandwidth ${UPLINK}kbit besteffort `get_cake_lla_string` ${EQDISC_OPTS}
}

egress() {

    LQ="quantum `get_htb_quantum $IFACE ${UPLINK}`"
    BURST="`get_htb_burst $IFACE ${UPLINK}`"

    $TC qdisc del dev $IFACE root 2>/dev/null

    case $QDISC in
        cake*) cake_egress; return ;;
    esac

    $TC qdisc add dev $IFACE root handle 1: `get_stab_string` htb default 10
    $TC class add dev $IFACE parent 1: classid 1:1 htb $LQ rate ${UPLINK}kbit ceil ${UPLINK}kbit $BURST `get_htb_adsll_string`
    $TC class add dev $IFACE parent 1:1 classid 1:10 htb $LQ rate ${UPLINK}kbit ceil ${UPLINK}kbit $BURST prio 0 `get_htb_adsll_string`
    $TC qdisc add dev $IFACE parent 1:10 handle 110: $QDISC \
        `get_limit ${ELIMIT}` `get_target "${ETARGET}" ${UPLINK}` `get_ecn ${EECN}` `get_flows ${UPLINK}` ${EQDISC_OPTS}

}

cake_ingress()
{
    $TC qdisc add dev $DEV root `get_stab_string` cake bandwidth ${DOWNLINK}kbit besteffort `get_cake_lla_string` ${IQDISC_OPTS}
    $IP link set dev $DEV up

    # redirect all IP packets arriving in $IFACE to $DEV

    $TC filter add dev $IFACE parent ffff: protocol all prio 10 u32 \
        match u32 0 0 flowid 1:1 action mirred egress redirect dev $DEV
}

ingress() {
    sqm_debug "ingress"
    $TC qdisc del dev $IFACE handle ffff: ingress 2>/dev/null
    $TC qdisc add dev $IFACE handle ffff: ingress

    LQ="quantum `get_htb_quantum $IFACE ${DOWNLINK}`"
    BURST="`get_htb_burst $IFACE ${DOWNLINK}`"

    $TC qdisc del dev $DEV root 2>/dev/null

    case $QDISC in
        cake*) cake_ingress; return ;;
    esac

    $TC qdisc add dev $DEV root handle 1: `get_stab_string` htb default 10
    $TC class add dev $DEV parent 1: classid 1:1 htb $LQ rate ${DOWNLINK}kbit ceil ${DOWNLINK}kbit $BURST `get_htb_adsll_string`
    $TC class add dev $DEV parent 1:1 classid 1:10 htb $LQ rate ${DOWNLINK}kbit ceil ${DOWNLINK}kbit $BURST prio 0 `get_htb_adsll_string`

    # FIXME: I'd prefer to use a pre-nat filter but we need to detect if nat is on this interface
    # AND we need to permute by a random number which we can't do from userspace filters

    # Most high rate flows are REALLY close. This stomps on those harder, but hurts on high rate long distance
    #$TC qdisc add dev $DEV parent 1:10 handle 110: $QDISC limit $LIMIT $ECN interval 20ms target 3ms `get_flows ${DOWNLINK}`
    $TC qdisc add dev $DEV parent 1:10 handle 110: $QDISC \
        `get_limit ${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_flows ${DOWNLINK}` ${IQDISC_OPTS}

    $IP link set dev $DEV up

    # redirect all IP packets arriving in $IFACE to ifb0

    $TC filter add dev $IFACE parent ffff: protocol all prio 10 u32 \
        match u32 0 0 flowid 1:1 action mirred egress redirect dev $DEV

}

sqm_start() {
    [ -n "$IFACE" ] || return 1
    do_modules
    verify_qdisc "htb" || return 1
    verify_qdisc $QDISC || return 1
    sqm_debug "Starting ${SCRIPT}"

    [ -z "$DEV" ] && DEV=$( get_ifb_for_if ${IFACE} )


    if [ "$UPLINK" -ne 0 ]; then
        egress
        sqm_debug "egress shaping activated"
    else
        sqm_debug "egress shaping deactivated"
        $TC qdisc del dev $IFACE root 2> /dev/null
    fi
    if [ "$DOWNLINK" -ne 0 ]; then
        verify_qdisc ingress "ingress" || return 1
        ingress
        sqm_debug "ingress shaping activated"
    else
        sqm_debug "ingress shaping deactivated"
        $TC qdisc del dev $DEV root 2> /dev/null
        $TC qdisc del dev $IFACE ingress 2> /dev/null
    fi

    return 0
}

################################################################################
